package com.nip.commons.xmlconfig;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.activation.UnsupportedDataTypeException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import com.nip.commons.xmlconfig.xml.DomHelper;

public abstract class XmlConfigBase {

    private final DocumentBuilderFactory dbFactory = DocumentBuilderFactory
            .newInstance();
    private DocumentBuilder dBuilder;
    private Document doc;
    private NodeList nodes;
    private final DomHelper helper = new DomHelper();
    private Map<String, List<Map<String, String>>> mapCache = new HashMap<String, List<Map<String, String>>>();
    private String rootName = "config";
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    protected String cfgPath = null;
    private final ScheduledExecutorService service = Executors
            .newScheduledThreadPool(1);
    private ScheduledFuture<?> realoadSchedule;

    public XmlConfigBase() {
        super();
        // TODO Auto-generated constructor stub
    }

    public XmlConfigBase(String rootName) {
        super();
        this.rootName = rootName;
    }

    public void load(String cfgpath) throws Exception {
        this.cfgPath = cfgpath;
        InputStream is = new FileInputStream(cfgpath);
        load(is);
    }

    public void load(InputStream is) throws Exception {
        try {
            readWriteLock.writeLock().lock();
            if (dBuilder != null) {
                dBuilder = null;
            }

            if (doc != null) {
                doc = null;
            }

            if (nodes != null) {
                nodes = null;
            }

            dBuilder = dbFactory.newDocumentBuilder();
            doc = dBuilder.parse(is);
            nodes = doc.getElementsByTagName(rootName).item(0).getChildNodes();
            mapCache.clear();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public synchronized boolean startAutoReload(long timeReload)
            throws UnsupportedDataTypeException {
        if (this.cfgPath == null || this.cfgPath.equals("")) {
            throw new UnsupportedDataTypeException(
                    "Cannot support resource is inputstream");
        }
        try {

            if (realoadSchedule != null) {
                try {
                    realoadSchedule.cancel(true);
                } catch (Exception e) {

                }
            }

            realoadSchedule = service.scheduleWithFixedDelay(autoReloadCfg, 0,
                    timeReload, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    protected String getValue(String tag_name) {
        try {
            readWriteLock.readLock().lock();
            return helper
                    .getNodeValue(helper.getNodeSequenTag(tag_name, nodes));
        } finally {
            readWriteLock.readLock().unlock();
        }
    }

    protected String getValue(String tag_name, String defaultValue) {
        try {
            readWriteLock.readLock().lock();
            String value = helper.getNodeValue(helper.getNodeSequenTag(
                    tag_name, nodes));
            return value == null ? defaultValue : value;
        } catch (Exception e) {
            return defaultValue;
        } finally {
            readWriteLock.readLock().unlock();
        }
    }

    protected int getIntValue(String tag_name) {
        return Integer.parseInt(helper.getNodeValue(helper.getNodeSequenTag(
                tag_name, nodes)));
    }

    protected int getIntValue(String tag_name, int defaultValue) {
        try {
            return Integer.parseInt(helper.getNodeValue(helper
                    .getNodeSequenTag(tag_name, nodes)));
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected float getFloatValue(String tag_name) {
        return Float.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                tag_name, nodes)));
    }

    protected float getFloatValue(String tag_name, float defaultValue) {
        try {
            return Float.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                    tag_name, nodes)));
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected double getDoubleValue(String tag_name) {
        return Double.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                tag_name, nodes)));
    }

    protected double getDoubleValue(String tag_name, double defaultValue) {
        try {
            return Double.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                    tag_name, nodes)));
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected long getLongValue(String tag_name) {
        return Long.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                tag_name, nodes)));
    }

    protected long getLongValue(String tag_name, long defaultValue) {
        try {
            return Long.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                    tag_name, nodes)));
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected boolean getBoeanValue(String tag_name) {
        return Boolean.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                tag_name, nodes)));
    }

    protected boolean getBoeanValue(String tag_name, boolean defaultValue) {
        try {
            return Boolean.valueOf(helper.getNodeValue(helper.getNodeSequenTag(
                    tag_name, nodes)));
        } catch (Exception e) {
            return defaultValue;
        }
    }

    protected List<Map<String, String>> getMapValues(String tag_name,
            String... tag_ms) {
        try {
            readWriteLock.readLock().lock();
            if (mapCache.get(tag_name) != null) {
                return mapCache.get(tag_name);
            }

            NodeList nodelist = nodes;

            int index = tag_name.lastIndexOf(".");
            String nodeName = tag_name;
            if (index != -1) {
                nodelist = helper.getNodesSequenTag(
                        tag_name.substring(0, index), nodes);
                nodeName = tag_name.substring(index + 1, tag_name.length());
            }

            if (nodelist.getLength() == 0) {
                throw new NullPointerException("Value of " + tag_name
                        + " not found.");
            }

            List<Map<String, String>> listValue = new ArrayList<Map<String, String>>();
            Map<String, String> mapValue;
            NodeList nodeListTemp;

            for (int i = 0; i < nodelist.getLength(); i++) {
                if (!nodeName.equalsIgnoreCase(nodelist.item(i).getNodeName())) {
                    continue;
                }

                nodeListTemp = nodelist.item(i).getChildNodes();

                mapValue = new HashMap<String, String>();

                for (String tag : tag_ms) {
                    mapValue.put(tag, helper.getNodeValue(helper
                            .getNodeSequenTag(tag, nodeListTemp)));
                }
                listValue.add(mapValue);

            }

            mapCache.put(tag_name, listValue);
            return listValue;
        } catch (Exception e) {
            return null;
        } finally {
            readWriteLock.readLock().unlock();
        }

    }

    private final Runnable autoReloadCfg = new Runnable() {

        @Override
        public void run() {

            try {
                load(cfgPath);
            } catch (Exception e) {
            }
        }
    };

}
